package com.fowo.api.sys.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.fowo.api.common.model.RException;
import com.fowo.api.sys.config.FileServiceProperties;
import com.fowo.api.sys.entity.SysFile;
import com.fowo.api.sys.model.CreateSysFile;
import com.fowo.api.sys.service.FileExtHandler;
import com.fowo.api.sys.service.FileService;
import com.fowo.api.sys.service.FileStorageService;
import com.fowo.api.sys.service.SysFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class FileServiceImpl implements FileService, InitializingBean {
    @Resource
    private Map<String, FileStorageService> storageServiceMap;
    @Resource
    private List<FileExtHandler> fileExtHandlers;
    @Resource
    private SysFileService sysFileService;
    @Resource
    private FileServiceProperties serviceProperties;

    @Override
    public InputStream read(SysFile sysFile) {
        FileStorageService fileStorageService = storageServiceMap.get(sysFile.getStorageType());
        try {
            return fileStorageService.read(sysFile.getStorageKey());
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
    }

    @Override
    public InputStream readDirect(String storageType, String storageKey) {
        FileStorageService fileStorageService = storageServiceMap.get(storageType);
        try {
            return fileStorageService.read(storageKey);
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
    }

    @Override
    public FileStorageService getUploadStorageService() {
        for (Map.Entry<String, FileStorageService> kv : storageServiceMap.entrySet()) {
            if (serviceProperties.getUploadStorageService().isAssignableFrom(kv.getValue().getClass())) {
                return kv.getValue();
            }
        }
        return null;
    }

    @Override
    public SysFile upload(MultipartFile file, String ext) throws Exception {
        if (file.getSize() == 0) {
            throw new RException("上传文件长度为零");
        }
        return this.upload(file.getOriginalFilename(), file.getContentType(), file.getInputStream(), file.getSize(), ext);
    }

    @Override
    public SysFile upload(String filename, String contentType, InputStream inputStream, Long fileSize, String ext) throws Exception {
        Map.Entry<String, FileStorageService> storageService = getUploadStorageService(filename, ext);
        if (storageService == null) {
            throw new RException("未找到匹配的文件存储服务");
        }
        final String storageKey = storageService.getValue().write(filename, inputStream);
        CreateSysFile sysFile = new CreateSysFile();
        sysFile.setFilename(filename);
        sysFile.setFileSize(fileSize);
        sysFile.setMimeType(contentType);
        sysFile.setStorageType(storageService.getKey());
        sysFile.setStorageKey(storageKey);
        sysFile.setExt(ext);
        SysFile savedFile = sysFileService.create(sysFile);

        runExtHandles(savedFile);

        return savedFile;
    }

    private void runExtHandles(SysFile savedFile) {
        if(!StringUtils.hasText(savedFile.getExt())) {
            return;
        }
        try {
            JSONArray cmdArray = JSONArray.parseArray(savedFile.getExt());
            for (int i = 0; i < cmdArray.size(); i++) {
                String cmd = cmdArray.getString(i);
                String[] cmdAndArg = cmd.split(",");
                String name = cmdAndArg[0];
                String[] args = Arrays.copyOfRange(cmdAndArg, 1, cmdAndArg.length);
                int finalI = i;
                fileExtHandlers.stream().filter(h -> Objects.equals(h.name(), name)).findFirst()
                        .ifPresentOrElse(h -> {
                            try {
                                cmdArray.set(finalI, h.run(savedFile, args));
                            } catch (Exception e) {
                                log.error("运行上传扩展处理器命令 “{}” 时发生异常", cmd, e);
                            }
                        }, () -> log.warn("未找到命名为 {} 的文件扩展处理器", name));
            }
            // 更新附件上的扩展数据
            String ext = cmdArray.toJSONString();
            savedFile.setExt(ext);
            SysFile updateFile = new SysFile();
            updateFile.setId(savedFile.getId());
            updateFile.setExt(ext);
            sysFileService.updateById(updateFile);
        } catch (Exception e) {
            log.error("运行上传扩展处理器时发生异常", e);
        }
    }

    /**
     * 选择用于上传的文件存储服务
     * @param filename
     * @param ext
     * @return
     */
    private Map.Entry<String, FileStorageService> getUploadStorageService(String filename, String ext) {
        for (Map.Entry<String, FileStorageService> kv : storageServiceMap.entrySet()) {
            if (serviceProperties.getUploadStorageService().isAssignableFrom(kv.getValue().getClass())) {
                return kv;
            }
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (storageServiceMap == null || storageServiceMap.isEmpty()) {
            throw new Exception("配置不正常，文件服务未找到任何可用文件存储服务！");
        }
    }
}
